package cn.iocoder.yudao.module.dify.service.translationtaskfile;

import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.dify.controller.admin.translationtaskfile.vo.TranslationTaskFilePageReqVO;
import cn.iocoder.yudao.module.dify.controller.admin.translationtaskfile.vo.TranslationTaskFileRespVO;
import cn.iocoder.yudao.module.dify.controller.admin.translationtaskfile.vo.TranslationTaskFileSaveReqVO;
import cn.iocoder.yudao.module.dify.controller.admin.translationtaskrecords.vo.TranslateWorkflowTextTaskData;
import cn.iocoder.yudao.module.dify.dal.dataobject.translationtaskfile.TranslationTaskFileDO;
import cn.iocoder.yudao.module.dify.dal.dataobject.translationtaskrecords.TranslationTaskRecordsDO;
import cn.iocoder.yudao.module.dify.dal.mysql.translationtaskfile.TranslationTaskFileMapper;
import cn.iocoder.yudao.module.dify.dal.mysql.translationtaskrecords.TranslationTaskRecordsMapper;
import cn.iocoder.yudao.module.dify.service.translationtaskrecords.TranslationTaskRecordsService;
import cn.iocoder.yudao.module.dify.util.FileContentReaderUtils;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.util.Collections;
import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * 翻译工作流任务文件 Service 实现类
 *
 * @author 芋道源码
 */
@Slf4j
@Service
@Validated
public class TranslationTaskFileServiceImpl implements TranslationTaskFileService {

    @Autowired
    private TranslationTaskRecordsService translationTaskRecordsService;

    @Resource
    private FileApi fileApi;

    @Resource
    private TranslationTaskFileMapper translationTaskFileMapper;

    // 生产者 - 将需要翻译文件放入Redis队列
    @Autowired
    @Qualifier("stringRedisTemplate")  // 或 "justAuthRedisCacheTemplate"
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private TranslationTaskRecordsMapper translationTaskRecordsMapper;

    private static final String TRANSLATION_TASK_DOCX_QUEUE = "translation:task:queue";
    private static final String ERROR_TRANSLATION_TASK_DOCX_QUEUE = "error:translation:task:queue";

    @Override
    public Long createTranslationTaskFile(TranslationTaskFileSaveReqVO createReqVO) {
        // 插入
        TranslationTaskFileDO translationTaskFile = BeanUtils.toBean(createReqVO, TranslationTaskFileDO.class);
        translationTaskFileMapper.insert(translationTaskFile);
        // 返回
        return translationTaskFile.getId();
    }

    @Override
    public void updateTranslationTaskFile(TranslationTaskFileSaveReqVO updateReqVO) {
        // 校验存在
        validateTranslationTaskFileExists(updateReqVO.getId());
        // 更新
        TranslationTaskFileDO updateObj = BeanUtils.toBean(updateReqVO, TranslationTaskFileDO.class);
        translationTaskFileMapper.updateById(updateObj);
    }

    @Override
    public void deleteTranslationTaskFile(Long id) {
        // 校验存在
        validateTranslationTaskFileExists(id);
        // 删除
        translationTaskFileMapper.deleteById(id);
    }

    private void validateTranslationTaskFileExists(Long id) {
        if (translationTaskFileMapper.selectById(id) == null) {
            throw exception(new ErrorCode(1_800_000_002, "翻译文件不存在"));
        }
    }

    @Override
    public TranslationTaskFileDO getTranslationTaskFile(Long id) {
        return translationTaskFileMapper.selectById(id);
    }

    @Override
    public PageResult<TranslationTaskFileDO> getTranslationTaskFilePage(TranslationTaskFilePageReqVO pageReqVO) {
        return translationTaskFileMapper.selectPage(pageReqVO);
    }

    @Override
    public List<TranslationTaskFileRespVO> getTranslationTaskFileListByTaskId(Long id) {
        List<TranslationTaskFileDO> list = translationTaskFileMapper.selectList(TranslationTaskFileDO::getDifyTranslationTaskRecordsId, id);
        if (list != null) {
            return BeanUtils.toBean(list, TranslationTaskFileRespVO.class);
        }
        return Collections.emptyList();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean executeTranslateTask(Long taskId) {
        TranslationTaskRecordsDO translationTaskRecords = translationTaskRecordsMapper.selectById(taskId);
        List<TranslationTaskFileDO> list = translationTaskFileMapper.selectList(TranslationTaskFileDO::getDifyTranslationTaskRecordsId, taskId);
        for (TranslationTaskFileDO taskFile : list) {
            // 放入Redis队列
            redisTemplate.opsForList().leftPush(TRANSLATION_TASK_DOCX_QUEUE, taskFile.getId().toString());
            taskFile.setProcessStatus(1);
        }
        translationTaskRecords.setProcessStatus(1);
        translationTaskRecordsMapper.updateById(translationTaskRecords);
        translationTaskFileMapper.updateBatch(list);
        return true;
    }

    // 消费者 - 从Redis队列取出任务并处理
    @Scheduled(fixedDelay = 2000) // 或使用其他方式启动监听
    public void duringExecutionTranslationTasks() {
        String taskInfo = redisTemplate.opsForList().rightPop(TRANSLATION_TASK_DOCX_QUEUE);
        if (taskInfo != null) {
            TranslationTaskFileDO taskFile = translationTaskFileMapper.selectById(Long.parseLong(taskInfo));
            try {
                String originalUrl = taskFile.getOriginalUrl();
                // 执行翻译处理逻辑
                boolean docxFile = FileContentReaderUtils.isDocxFile(originalUrl);
                log.info("处理文件: " + originalUrl);
                if (docxFile) {
                    // 编码URL中的特殊字符
                    String encodedUrl = FileContentReaderUtils.encodeUrl(originalUrl);

                    // 下载文件内容
                    byte[] fileContent = FileContentReaderUtils.downloadFileFromUrl(encodedUrl);
                    // 加载DOCX文档
                    XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(fileContent));
                    // 翻译文档
                    TranslateWorkflowTextTaskData translatedDocument = translationTaskRecordsService.translateDocxFile(document, taskFile.getCreator());

                    // 将翻译后的文档转换为字节数组
                    byte[] translatedBytes = FileContentReaderUtils.documentToByteArray(translatedDocument.getXwpfDocument());

                    // 保存翻译后的文档
                    String fileName = FileNameUtil.getName(originalUrl);
                    if (StrUtil.isAllNotBlank(fileName) && !StrUtil.isBlankOrUndefined(fileName)) {
                        fileName = FileNameUtil.getPrefix(fileName) + "-en." + FileNameUtil.getSuffix(fileName);
                    } else {
                        fileName = "translated_document.docx";
                    }
                    String enFileUrl = fileApi.createFile(fileName, translatedBytes);
                    taskFile.setMinioUrl(enFileUrl);
                    taskFile.setTotalTokens(translatedDocument.getTotalTokens());
                    taskFile.setProcessStatus(2);
                    translationTaskFileMapper.updateById(taskFile);
                    TranslationTaskRecordsDO translationTaskRecordsDO = translationTaskRecordsMapper.selectById(taskFile.getDifyTranslationTaskRecordsId());
                    translationTaskRecordsDO.setTotalTokens(translationTaskRecordsDO.getTotalTokens() + translatedDocument.getTotalTokens());

                    // 检查并更新任务状态,所有文件成功,任务改成成功
                    checkAndUpdateTaskStatus(taskFile.getDifyTranslationTaskRecordsId(), translationTaskRecordsDO);

                    translationTaskRecordsMapper.updateById(translationTaskRecordsDO);
                    log.info("翻译完成: " + enFileUrl);
                }
            } catch (Exception e) {
                // 处理失败时记录日志或重新入队
                log.error("翻译任务处理失败: " + taskInfo, e);
                // 放入失败队列
                redisTemplate.opsForList().leftPush(ERROR_TRANSLATION_TASK_DOCX_QUEUE, taskInfo);
            }
        }
    }

    /**
     * 检查并更新任务状态
     * 当所有文件的ProcessStatus都为2时，将任务的processStatus设置为2
     *
     * @param taskRecordsId 任务记录ID
     * @param translationTaskRecordsDO 任务记录对象
     */
    private void checkAndUpdateTaskStatus(Long taskRecordsId, TranslationTaskRecordsDO translationTaskRecordsDO) {
        try {
            // 获取该任务下所有文件
            List<TranslationTaskFileDO> translationTaskFileDOS = translationTaskFileMapper.selectList(
                    TranslationTaskFileDO::getDifyTranslationTaskRecordsId, taskRecordsId);

            if (translationTaskFileDOS == null || translationTaskFileDOS.isEmpty()) {
                log.warn("任务 {} 下没有找到任何文件", taskRecordsId);
                return;
            }

            // 检查所有文件的处理状态是否都为2（处理成功）
            boolean allFilesCompleted = translationTaskFileDOS.stream()
                    .allMatch(file -> file.getProcessStatus() != null && file.getProcessStatus() == 2);

            if (allFilesCompleted) {
                translationTaskRecordsDO.setProcessStatus(2);
                log.info("任务 {} 的所有 {} 个文件翻译完成，更新任务状态为完成",
                        taskRecordsId, translationTaskFileDOS.size());
            } else {
                // 记录当前状态用于调试
                long completedCount = translationTaskFileDOS.stream()
                        .mapToLong(file -> file.getProcessStatus() != null && file.getProcessStatus() == 2 ? 1 : 0)
                        .sum();
                log.debug("任务 {} 进度：{}/{} 个文件已完成",
                        taskRecordsId, completedCount, translationTaskFileDOS.size());
            }
        } catch (Exception e) {
            log.error("检查任务状态时发生异常，任务ID: {}", taskRecordsId, e);
        }
    }

}